/*
 * person.c
 *
 * Sisältää person rakenteen sekä funktioita joita käytetään 
 * muuttaessa tai selatesas henkilöön liittyviä asioita (lapset, suhteet, ikä jne)
 * 
 */
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <wchar.h>
#include "dynarray.h"
#include "relation.h"
#include "person.h"
#include "date.h"

/*
 * Lisää relaation person1 ja person2 välille
 * @return true, jos onnistui, muuten false
 */

bool per_add_relation(person* person1, person* person2, rel_t type) {//Joni, Ossi muokkasi/
    if (per_compare(person1, person2) != 0) {
        relation * rel = rel_new(person1, person2, type);

        if (per_has_relation(person1, rel) || per_has_relation(person2, rel)) { //ei lisätä samaa relaatiota
            rel_free(rel);
            return false;
        }

        return da_append(person1->relations, rel) && da_append(person2->relations, rel);
    } else return false;
}

/*
 *	Lisää parent1 ja parent2 välille child lapsen
 */
bool per_add_child(person* parent1, person* parent2, person* child) { //Joni
    if (per_compare(parent1, parent2) != 0) {
        return per_add_relation(parent1, child, REL_CHILD) && per_add_relation(parent2, child, REL_CHILD);
    } else return per_add_child_singleparent(parent1, child);
}

/*
 *	Lisää 'yksinhuoltajan' lapsen (lapsen toista vanhempaa ei tiedetä)
 */
bool per_add_child_singleparent(person* parent, person* child) { //Joni
    return per_add_relation(parent, child, REL_CHILD);
}

/*
 * Poistaa relaation.
 */
bool per_remove_relation(relation* _rel) { //Joni
    person *person1 = _rel->person1, *person2 = _rel->person2;
    int person1_rel_index = -1, person2_rel_index = -1;
    bool retvalue = true;
    for (unsigned int i = 0; i < person1->relations->top; ++i) {
        if (rel_equals(da_get(person1->relations, i), _rel)) {
            person1_rel_index = i;
            break;
        }
    }
    for (unsigned int i = 0; i < person2->relations->top; ++i) {
        if (rel_equals(da_get(person2->relations, i), _rel)) {
            person2_rel_index = i;
            break;
        }
    }
    retvalue = (retvalue && ((person1_rel_index != -1) && (person2_rel_index != -1)));
    if (retvalue) {
        // molemmissa henkilöissä on viittaus samaan relationiin, joten riittää vapauttaa toinen
        da_remove(person1->relations, (unsigned int) person1_rel_index);
        rel_free(da_remove(person2->relations, (unsigned int) person2_rel_index));
    }
    return retvalue;
}

bool per_has_relation(person* per, relation* r) { //Joni
    for (unsigned int i = 0; i < per->relations->top; ++i) {
        if (rel_equals(da_get(per->relations, i), r)) {
            return true;
        }
    }
    return false;
}

/**
 * Hakee child:in vanhemmat  
 * @return dynarray<person> parents
 */
dynarray* per_get_parents(person* child) { //Joni
    dynarray * parents = da_new();
    for (unsigned int i = 0; i < child->relations->top; ++i) {
        relation* rel = (relation*) da_get(child->relations, i);
        if (per_compare(rel->person2, child) == 0) {
            if (rel->type == REL_CHILD) {
                da_append(parents, rel->person1);
            }
        }
    }
    return parents;
}

/**
 * Hakee parent:in lapset
 * @return dynarray<person> children
 */
dynarray* per_get_children(person* parent) { //Joni
    dynarray * children = da_new();
    for (unsigned int i = 0; i < parent->relations->top; ++i) {
        relation* rel = (relation*) da_get(parent->relations, i);
        if (per_compare(rel->person1, parent) == 0) {
            if (rel->type == REL_CHILD) {
                da_append(children, rel->person2);
            }
        }
    }
    return children;
}

/**
 * Hakee person:in kumppanin/t
 * @return dynarray<person> partner
 */
dynarray* per_get_partner(person* person1) { //Joni
    dynarray * partner = da_new();
    for (unsigned int i = 0; i < person1->relations->top; ++i) {
        relation* rel = (relation*) da_get(person1->relations, i);
        if (rel->type == REL_MARRIAGE) {
            if (per_compare(rel->person1, person1) == 0) {
                da_append(partner, rel->person2);
            } else {
                da_append(partner, rel->person1);
            }
            break;
        }
    }
    return partner;
}

/**
 * Hakee personin muut suhteet
 * @return dynarray<person> others
 */
dynarray* per_get_other_rel(person* person1) { //Ossi, copypastella !
    dynarray * partner = da_new();
    for (unsigned int i = 0; i < person1->relations->top; ++i) {
        relation* rel = (relation*) da_get(person1->relations, i);
        if (rel->type == REL_OTHER) {
            if (per_compare(rel->person1, person1) == 0) {
                da_append(partner, rel->person2);
            } else {
                da_append(partner, rel->person1);
            }
            break;
        }
    }
    return partner;
}

/*
 *	Hakee personin kaikki jälkeläiset (lapset, lapsenlapset ...)
 *	@return dynarray<person> offspring
 */
dynarray* per_get_offspring(person* person, dynarray * offspring) { //Joni
    if (offspring == NULL) {
        offspring = da_new();
    }
    dynarray* children = per_get_children(person);
    for (unsigned int i = 0; i < children->top; ++i) {
        da_append_inorder(offspring, da_get(children, i), per_compare, false);
        per_get_offspring(da_get(children, i), offspring);
    }
    da_free(children);
    return offspring;
}

/*
 *	Hakee personin kaikki isovanhemmat (vanhemmat, vanhempien vanhemmat ...)
 *	@return dynarray<person> ancestor
 */
dynarray* per_get_ancestor(person* person, dynarray * ancestor) { //Joni
    if (ancestor == NULL) {
        ancestor = da_new();
    }
    dynarray* parents = per_get_parents(person);
    for (unsigned int i = 0; i < parents->top; ++i) {
        da_append_inorder(ancestor, da_get(parents, i), per_compare, false);
        per_get_ancestor(da_get(parents, i), ancestor);
    }
    da_free(parents);
    return ancestor;
}

/*
 *	Vapauttaa personin muistista
 */
void per_free(person* person) { //Joni
    for (unsigned int i = 0; i < person->relations->top; ++i) {
        relation* r = da_get(person->relations, i);
        if (r != NULL) {
            per_remove_relation(r);
        }
    }
    free(person->name);
    da_free(person->relations);
    free(person);
}

/*
 *	Vertailee kahta henkilöä toisiinsa (nimen perusteella)
 */
int per_compare(void* person1, void* person2) { //Ossi
    person* p1 = (person*) person1, *p2 = (person*) person2;
    return wcscmp(p1->name, p2->name);
}

/*
 *	Asettaa henkilön syntymäpäivän
 */
void per_set_deathday(person* person, date deathdate) { //Joni
    if (person != NULL) {
        person->deathdate = deathdate;
    }
}

/*
 *	Asettaa henkilön kuolinpäivän
 */
void per_set_birthday(person* person, date birthdate) { //Joni
    if (person != NULL) {
        person->birthdate = birthdate;
    }
}
